/*
 * JBoss, Home of Professional Open Source
 *
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * by the @author tags. See the COPYRIGHT.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.netty.handler.codec.bayeux;

import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineCoverage;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;

/**
 *
 * @author daijun
 */
@ChannelPipelineCoverage("one")
public class BayeuxDecoder extends OneToOneDecoder {

    private static final InternalLogger logger =
            InternalLoggerFactory.getInstance(BayeuxDecoder.class.getName());
    private State state = State.UNCONNECTED;

    /**
     * The internal state of {@link BayeuxDecoder}.
     * <em>Internal use only</em>.
     *
     */
    protected enum State {

        UNCONNECTED,
        CONNECTING,
        CONNECTED,
    }

    public BayeuxDecoder() {
    }


    @Override
    protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
        logger.info("Begin Bayeux Decoding...");
        if (!(msg instanceof HttpRequest)) {
            return msg;
        }

        HttpRequest request = (HttpRequest) msg;
        HttpMethod method = request.getMethod();
        HttpVersion version = request.getProtocolVersion();
        String paramValue = null;
        if (HttpMethod.POST == method && HttpVersion.HTTP_1_1 == version) {//callback polling
            String charset = "utf-8";
            if (request.containsHeader(HttpHeaders.Names.CONTENT_TYPE)) {
                String contentType = request.getHeader(HttpHeaders.Names.CONTENT_TYPE);
                charset = contentType.substring(contentType.indexOf("charset") + 8);
            }
            if (Charset.forName(charset) == null) {
                throw new NullPointerException("charset=" + charset);
            }
            logger.info("Content charset=" + charset);

            String httpContent = ((ChannelBuffer) request.getContent()).toString(charset);
            httpContent = URLDecoder.decode(httpContent, charset);
            logger.info("HTTP POST content: " + httpContent);

            int begin = httpContent.indexOf("message=");
            if (begin == -1) {
                paramValue = httpContent;
                //return msg;
            } else {
                int end = httpContent.indexOf("\n", begin) == -1 ? httpContent.length() : httpContent.indexOf("\n", begin);
                paramValue = httpContent.substring(begin + 8, end);
            }

        } else if (HttpMethod.GET == method) {//Long polling
            QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
            Map<String, List<String>> params = queryStringDecoder.getParameters();
            if (!params.isEmpty()) {
                List<String> vals = params.get("message");
                for (String val : vals) {
                    paramValue += val;
                }
            } else {
                return msg;
            }
        } else {
            return msg;
        }

        logger.info("paramValue=" + paramValue);
        Object result = new JSONParser().parse(paramValue);
        if (result == null) {
            return null;
        } else if (result instanceof Object[] && ((Object[]) result).length > 0) {
            result = ((Object[]) result)[0];
        }
        BayeuxMessageFactory factory = BayeuxMessageFactory.getInstance();
        BayeuxMessage bayeux = factory.create((Map) result);
        if (bayeux instanceof HandshakeRequest) {
            return (HandshakeRequest) bayeux;
        } else if (bayeux instanceof ConnectRequest) {
            return (ConnectRequest) bayeux;
        } else if (bayeux instanceof DisconnectRequest) {
            return (DisconnectRequest) bayeux;
        }

        return result;
    }
}
